﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

using BFTest.Ammunitions;
using BFTest.Collision;

namespace BFTest.Classes
{
    public enum UnitClass : int
    {
        Rifleman,
        MachineGunner,
        Assaultman
    };

    public enum UnitRank : int
    {
        Private,
        Corporal,
        Sergeant,
        Luitenant,
        Captain,
        Colonel,
        General
    }

    public enum UnitStatus : int
    {
        Dead,
        Idle,
        Reloading
    }

    abstract class CUnit : ICollideable
    {
        int side;
        public int Side { get { return side; } private set { side = value; } }

        UnitStatus status;
        public UnitStatus Status { get { return status; } set { status = value; if (status == UnitStatus.Dead) alive = false; } }

        int id;
        public int ID { get { return id; } private set { id = value; } }

        string soldierName;
        public string SoldierName { get { return soldierName; } private set { soldierName = value; } }

        UnitRank rank;
        public UnitRank Rank { get { return rank; } set { rank = value; } }

        int experience;
        public int Experience { get { return experience; } set { experience = value; } }

        int health;
        public int Health { get { return health; } private set { health = value; } }

        bool alive;
        public bool Alive { get { return alive; } private set { alive = value; if (!alive) status = UnitStatus.Dead; } }

        Texture2D texture;
        public Texture2D Texture { get { return texture; } private set { texture = value; } }
        
        Vector2 textureOrigin;

        Vector2 position;
        public Vector2 Position { get { return position; } private set { position = value; UpdateBoundingBox(); } }

        Vector2 velocity;
        public Vector2 Velocity { get { return velocity; } set { velocity = value; } }

        Vector2 direction;
        public Vector2 Direction { get { return direction; } set { direction = value; } }

        public float Rotation { get { return Direction.Rotation(); } }

        bool selected;
        public bool Selected { get { return selected; } set { selected = value; } }
        
        TimeSpan lastShot;
        protected TimeSpan LastShot { get { return lastShot; } set { lastShot = value; } }

        CUnit target;
        public CUnit Target { get { return target; } set { target = value; } }

        Rectangle aaBoundingBox;
        public Rectangle AABoundingBox { get { return aaBoundingBox; } private set { aaBoundingBox = value; } }

        public float BoundingRadius { get { return AABoundingBox.Width / 2; } }

        CMessageList messages = new CMessageList();
        public CMessageList Messages { get { return messages; } private set { messages = value; } }

        abstract public int MaxHealth { get; }
        abstract public float Size { get; }
        abstract public string ClassName { get; }
        abstract public Color ClassColor { get; }
        
        // shouldn't this be moved to ShootingClass? - stoop
        abstract public void Shoot(Vector2 target);

        public CUnit(int id, Vector2 position, string soldierName, UnitRank rank, int side, Texture2D texture)
        {
            ID = id;
            Position = position;
            SoldierName = soldierName;
            Rank = rank;
            Alive = true;
            Status = UnitStatus.Idle;
            Side = side;
            Texture = texture;
            textureOrigin = new Vector2((float)Texture.Width / 2, (float)Texture.Height / 2);
            
            this.Initialize();
        }

        protected virtual void Initialize()
        {
            Health = MaxHealth;
        }

        public virtual void Update(GameTime gameTime)
        {
        }

        protected virtual void DrawBefore(SpriteBatch spriteBatch)
        {
            // Draw the unit's outline
            float outlineSizeMultiplier = 1.5f;
            Color outlineColor = Color.Black;

            if (Selected == true)
                outlineColor = Color.Orange;
            else
                if (Side == 1)
                    outlineColor = Color.Blue;
                else if (Side == 2)
                    outlineColor = Color.Red;

            spriteBatch.Draw(BFTestGame.Battlefield.Pixel, Position, null, outlineColor, Rotation, textureOrigin, Size * outlineSizeMultiplier, SpriteEffects.None, 0f);
        }

        protected virtual void DrawAfter(SpriteBatch spriteBatch)
        {
            // Draw the unit as black if they're dead
            if (!Alive)
            {
                spriteBatch.Draw(Texture, Position, null, Color.Black, Rotation, textureOrigin, Size, SpriteEffects.None, 0f);
            }
        }

        public virtual void Draw(SpriteBatch spriteBatch)
        {
            // This code for finding the origin will not work once we use real sprites.
            
            spriteBatch.Draw(Texture, Position, null, ClassColor, Rotation, textureOrigin, Size, SpriteEffects.None, 0f);
        }

        protected void UpdateBoundingBox()
        {
            AABoundingBox = new Rectangle(
                (int)(Position.X - (Size / 2)),
                (int)(Position.Y - (Size / 2)),
                (int)Size, (int)Size);
        }

        public bool ModifyHealth(int change)
        {
            if (Alive)
            {
                Health += change;

                if (Health < 0)
                    Health = 0;
                if (Health > MaxHealth)
                    Health = MaxHealth;

                if (Health == 0)
                {
                    Alive = false;
                    Status = UnitStatus.Dead;
                    return true;
                }
            }
            return false;
        }

        public float DistanceToUnit(CUnit unit)
        {
            return Vector2.Distance(Position, unit.Position);
        }

        public float DirectionToUnit(CUnit unit)
        {
            Vector2 direction = unit.Position - Position;

            return (float)Math.Atan2(direction.Y, direction.X);
        }

        // Since set position is private, I'm creating an acessor that's only available to CollisionManagers - stoop
        public void SetPosition(CollisionManagerGC manager, Vector2 position)
        {
            if(manager != null)
                Position = position;
            throw new ArgumentNullException("manager", "Must pass Valid CollisionManager to SetPosition Method");
        }
    }
}
